
/**
 * Copyright 2009 cestum.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.waveprotocol.wave.model.document.operation.automaton.violation {

import org.goverla.collections.ArrayList;
import org.goverla.collections.ListCollectionViewIterator;
import org.waveprotocol.wave.model.document.operation.automaton.ValidationResult;
import org.waveprotocol.wave.model.util.StringBuilder;

/**
   * A class to hold a set of violations.  The checkXXX methods take this
   * as an input parameter and add violations to it if there are any.
   */
  // I'm not particularly proud of the design of this class.
  public final class ViolationCollector {
    private var operationIllFormed:ArrayList = new ArrayList();
    private var operationInvalid:ArrayList = new ArrayList();
    private var schemaViolations:ArrayList = new ArrayList();
    
    //For overloaded add methods
	 public function add(o:Object) {  
         // figure out what the type of o actually is and call our  
         // private helper function based on that  
         if (o  instanceof OperationIllFormed) {  
             addIllFormed(OperationIllFormed(o));  
         } else if (o instanceof OperationInvalid) {  
             addInvalid(OperationInvalid(o));  
         } else if ( o instanceof  SchemaViolation) {  
             addSchemaViolation(SchemaViolation(o));  
         } else {  
             // run-time error checking, invalid type  
             // for now I'll just trace the output  
             throw new Error("Violation Collector invalid type " + typeof(o));  
               
             // it would be preferable to throw an exception here, but explaining  
             // this line is beyond the scope of this entry..  
             //throw new Error("overloadedMethod1 invalid type " + typeof(o));  
         }  
    }  


    public function addIllFormed(v:OperationIllFormed):void {
      operationIllFormed.addItem(v);
    }
    public function addInvalid(v:OperationInvalid):void {
      operationInvalid.addItem(v);
    }
    public function addSchemaViolation(v:SchemaViolation):void {
      schemaViolations.addItem(v);
    }
    /** True iff at least one violation of the well-formedness constraints was detected. */
    public function isIllFormed():Boolean {
      return getValidationResult().isIllFormed();
    }
    /**
     * True iff the most severe validation constraint detected was
     * {@link ValidationResult#INVALID_DOCUMENT}
     */
    public function isInvalidDocument():Boolean {
      return getValidationResult().isInvalidDocument();
    }
    /**
     * True iff the most severe validation constraint detected was
     * {@link ValidationResult#INVALID_SCHEMA}
     */
    public function isInvalidSchema():Boolean {
      return getValidationResult().isInvalidSchema();
    }
    /** True iff there were no violations. */
    public function isValid():Boolean {
      return getValidationResult().isValid();
    }
    /** The merged (most severe) validation result */
    public function getValidationResult():ValidationResult {
      if (!operationIllFormed.length == 0) {
        return ValidationResult.ILL_FORMED;
      } else if (!operationInvalid.length == 0) {
        return ValidationResult.INVALID_DOCUMENT;
      } else if (!schemaViolations.length == 0) {
        return ValidationResult.INVALID_SCHEMA;
      } else {
        return ValidationResult.VALID;
      }
    }

    /** Returns a description of a single violation, or null if there are none. */
    public function firstDescription():String {
	  var itr1:ListCollectionViewIterator =  operationIllFormed.createIterator();
	  while(itr1.hasNext()) {
		  var v:OperationIllFormed = OperationIllFormed(itr1.next());
		  return "ill-formed: " + v.description();
	  }
	  var itr2:ListCollectionViewIterator =  operationInvalid.createIterator();
	  while(itr2.hasNext()) {
		  var v2:OperationInvalid = OperationInvalid(itr2.next());
		  return "ill-formed: " + v2.description();
	  }
	  var itr3:ListCollectionViewIterator =  schemaViolations.createIterator();
	  while(itr3.hasNext()) {
		  var v3:SchemaViolation = SchemaViolation(itr3.next());
		  return "ill-formed: " + v3.description();
	  }
	  return null;
    }

//    /** Prints descriptions of violations that have been detected. */
//    public function printDescriptions(out:PrintStream):void {
//      printDescriptions(out, "");
//    }
//
//    /**
//     * Prints descriptions of violations that have been detected, prefixing
//     * each line of output with the given prefix.
//     */
//    public function printDescriptions(out:PrintStream, prefix:String):void {
//      if (isValid()) {
//        out.println(prefix + "no violations");
//        return;
//      }
//      for (var v:OperationIllFormed in operationIllFormed) {
//        out.println(prefix + "ill-formed: " + v.description());
//      }
//      for (var  v1:OperationInvalid in operationInvalid) {
//        out.println(prefix + "invalid operation: " + v1.description());
//      }
//      for (var v2:SchemaViolation in schemaViolations) {
//        out.println(prefix + "schema violation: " + v2.description());
//      }
//    }

    private function size():int {
      return operationIllFormed.length + operationInvalid.length + schemaViolations.length;
    }

    
    public function toString():String {
      if (size() == 0) {
        return "ViolationCollector[0]";
      }
      var b:StringBuilder= new StringBuilder();
      b.append("ViolationCollector[" +
          size() + ": " + firstDescription() + "]");
      return b.toString();
    }
  }
}